package org.goldenport.g3

import scala.collection.mutable
import scala.collection.mutable.{Stack, Map, HashMap, MultiMap, ArrayBuffer}
import scala.tools.nsc.{Interpreter}
import scala.tools.nsc.util.BatchSourceFile
import scala.util.matching.Regex
import java.lang.reflect.{Method, Modifier}
import java.io.File
import java.net.{URL, URI}
import java.sql.ResultSet
import java.sql.Types._
import javax.servlet.http.HttpServletResponse.SC_OK
import javax.servlet.http.HttpServletResponse
import com.asamioffice.goldenport.io.UFile
import com.asamioffice.goldenport.util.RegexRegistry
import org.goldenport.Strings.mimetype
import org.goldenport.util.Control.or
import org.goldenport.parameter.GParameterRepository
import org.goldenport.service.GServiceContext
import org.goldenport.session.GSession
import org.goldenport.entity.{GEntity, GEntitySpace, DefaultEntitySpace, GEntityContext}
import org.goldenport.entity.datasource.{GDataSource, FileDataSource}
import org.goldenport.entity.locator.ResourceLocator
import org.goldenport.entity.content.{GContent, BinaryContent}
import org.goldenport.entities.zip.ZipEntityClass
import org.goldenport.entities.csv.CsvEntityClass
import org.goldenport.entities.xmind.XMindEntityClass
import org.goldenport.entities.graphviz.GraphvizEntity
import org.goldenport.entities.fs.FileStoreEntity
import org.goldenport.entities.html.HtmlRealmEntity
import org.goldenport.entities.smartdoc.SmartDocEntity
import org.goldenport.entities.specdoc.SpecDocEntity
import org.goldenport.entities.view.TreeViewEntity
import org.goldenport.entities.workspace.TreeWorkspaceEntity
import org.goldenport.g3.adapters._
import org.goldenport.g3.messages._
import org.goldenport.g3.messages.http.HttpCommand
import org.goldenport.g3.events.Exceptioned
import org.goldenport.g3.drivers.AppEngineDataStoreDriver
import org.goldenport.g3.jetty.JettyWebSocketDriver
import scalaz._
import Scalaz._
import com.asamioffice.goldenport.text.UString
import org.goldenport.sdoc.SDoc

/**
 * @since   Mar. 19, 2010
 *  version Jun. 25, 2011
 *  version Dec.  5, 2011
 * @version Oct. 20, 2012
 * @author  ASAMI, Tomoharu
 */
abstract class G3Context extends NotNull {
  val messageStore: G3MessageStore
  def current: G3Node
  def currentNode:G3Node

  def setParameters(params: GParameterRepository)
  def getParameter(key: String): Option[Any]

  def getParameterString(key: String, defaultValue: String): String = {
    getParameter(key) match {
      case Some(value) => value.toString
      case None => defaultValue
    }
  }

  def getParameterInt(key: String, defaultValue: Int): Int = {
    getParameter(key) match {
      case Some(value) => value.toString.toInt
      case None => defaultValue
    }
  }

  def setEntitySpace(space: GEntitySpace)
  val entitySpace: GEntitySpace
  val entityContext: GEntityContext
  def setPlatform(pf: G3Platform)
  def findPortChannel(uri: String): Option[G3Channel]
  def findPortChannel(args: List[AnyRef]): Option[G3Channel]
  def portChannels: List[PortChannel]
  def findTimerChannels(): List[G3Channel]

  def open()
  def close()
  def commit()
  def rollback()

  //
  def pushNode(node: G3Node)
  def popNode()
  def pushChannelNode(node: G3ChannelNode)
  def popChannelNode()
  def currentPipe:G3Pipe
  def pushPipe(pipe: G3Pipe)
  def popPipe()

  // from G3Channel.channel_stimulus
  def createUnitofworkSignalParcel(title:String, channel: G3Channel)(signal: AnyRef, session: G3Session):(G3Parcel, G3Ticket)

  // from G3Channel.channel_send
  def createUnitofworkParcel(title:String, channel: G3Channel)(source: G3Parcel):(G3Parcel, G3Ticket)

  // from G3Join
  def createContentParcel(title: String)(content: AnyRef, source: G3Parcel): G3Parcel

  // from GComponent.action
  def createContentAttachmentsParcel(title: String)(content: AnyRef, attachments: List[AnyRef], source: G3Parcel): G3Parcel

  // from G3Actionable
  def createExceptionParcel(title: String)(exception: Exception, source: G3Parcel): G3Parcel

  // from G3Split
  def createSplitParcels(title: String)(contents: List[AnyRef], source: G3Parcel): List[G3Parcel]

  def createAggregatedParcel(title: String)(content: AnyRef, attachments: List[AnyRef], sources: List[G3Parcel]): G3Parcel

  def send(dest: String, parcel: G3Parcel): (G3Ticket, G3Parcel)

  def sendAll(dests: List[String], parcel: G3Parcel): (List[G3Ticket], G3Parcel)

  def invoke(dest: String, parcel: G3Parcel): G3Parcel

  def subscribe(publishers: List[String], channel: SubscribeChannel)

  def sendTerminal(parcel: G3Parcel, channel: G3Channel)

  def resultParcel(ticket: G3Ticket): G3Parcel

  def getResultParcel(ticket: G3Ticket): Option[G3Parcel]

  def invokeBean(className: String, methodName: Option[String], args: Array[AnyRef]): AnyRef

  def getContent(path: String): Option[AnyRef]

  // ChannelFactory
  def createPlainChannel(): PlainChannel

  def createStartChannel(): StartChannel

  def createPortChannel(rule: String, description: Option[Description]): G3Channel

  def createTimerChannel(rule: String): G3Channel

  def createWatchChannel(rule: String): G3Channel

  def createSubscribeChannel(publishers: List[String]): SubscribeChannel

  def createAgentChannel(activity: PartialFunction[AnyRef, AnyRef]): AgentChannel

  def createAgent2Channel(activity: G3AgentContext => PartialFunction[AnyRef, AnyRef]): Agent2Channel

  def createServiceChannel(uri: String): ServiceChannel

  def createBeanChannel(className: String, methodName: Option[String]): BeanChannel

  def createComponentChannel(component: G3Component): ComponentChannel

  def createEntityChannel(driver: G3EntityDriver): G3Channel

  def createViewChannel(driver: G3ViewDriver): G3Channel

  def createEventChannel(driver: G3EventDriver): G3Channel

  def createHtmlChannel(html: Html): G3Channel

  def createWebSocketChannel(path: String): G3Channel

  def createChannel(adapter: G3Adapter): G3Channel

  //
  def createDataStoreDriver(uri: String = "",
                            username: String = "", password: String = "",
                            driver: String = ""): G3DataStoreDriver

  def getAuthentication(uri: String): Option[G3Authentication]

  def putAuthentication(uri: String, auth: G3Authentication)

  def getDefaultJdbcDriver(): String

  def getDefaultJdbcUrl(): String

  def getDefaultJdbcUsername(): String

  def getDefaultJdbcPassword(): String

  def getDefaultDatastore(): String

  def getDefaultHttpFormat(): String

  def setDefaultJdbcDriver(driver: String)

  def setDefaultJdbcUrl(url: String)

  def setDefaultDatastore(ds: String)

  def setDefaultHttpFormat(format: String)

  def getJdbcConnection(
    unitofwork: Option[G3UnitofworkId], // XXX
    uri: String, username: String, password: String, driver: String): java.sql.Connection

  def sqlDatatypeFactory(uri: String): DatatypeFactory

  def getSqlData(rs: ResultSet, index: Int, columntype: Int): AnyRef

  def makeSqlDataType(datatype: XDatatype, Multiplicity: Multiplicity = MOne, facets: List[XFacet] = Nil): sql.SqlDatatype

  def createWorkFile(): File

  def createWorkDirectory(): File

  def workDirectory: File

  def tmpDirectory: File

  def executeCommand(aCommandLine: String): Process

  def executeCommand(aCommandLine: String, envp: Array[String]): Process

  def executeCommand(aCommandLine: String, envp: Array[String], dir: File): Process

  def getResource(name: String): URL

  def loadClass(name: String): Class[_]
  
  def newInstance[T](name: String): T

  //
  def compileSource(file: File)
  def executeScript(file: File)
  def interpret(line: String)

  def httpGet(get: http.GET): G3Event
  def httpPut(put: http.PUT): G3Event
  def httpPost(post: http.POST): G3Event
  def httpDelete(delete: http.DELETE): G3Event
  def httpMkcol(mkcol: http.MKCOL): G3Event
  def httpCopy(copy: http.COPY): G3Event
  def httpMove(move: http.MOVE): G3Event

  def urlFetchString(url: URL, encoding: String = "utf-8"): String

  // logging XXX platform logging
  def logInfo(o: AnyRef, message: Any, data: Any = None)

  def logEnter(o: AnyRef, name: String, message: Any, data: Any = None)

  def logLeave(o: AnyRef, name: String, message: Any, data: Any = None)

  def logDebug(o: AnyRef, message: Any, data: Any = None)

  def logInfoData(o: AnyRef, message: Any)(data: => Any)

  def logEnterData(o: AnyRef, name: String, message: Any)(data: => Any)

  def logLeaveData(o: AnyRef, name: String, message: Any)(data: => Any)

  def logDebugData(o: AnyRef, message: Any)(data: => Any)

  // Goldenport Entity
  def reconstitute(uri: String): AnyRef

  def reconstitute(file: File): AnyRef

  def reconstitute(url: URL): AnyRef

  def reconstituteEntity(any: AnyRef): Option[GEntity]

  def openResultSet(rs: ResultSet)

  def createEntity(gentity: GEntity)

  def openEntity(gentity: GEntity)

  def getDataSource(uri: String): GDataSource

  def getDataSource(uri: URI): GDataSource

  //
  def title: String

  def setTitle(title: String)

  def masterHtml: Html

  def findPage(uri: String): Option[G3HttpResponse]

  def masterCss(): String
}

class RootContext() extends G3Context {
  var defaultJdbcDriver = "org.apache.derby.jdbc.EmbeddedDriver"
  var defaultJdbcUrl = "jdbc:derby:target/g3/derbyDB;create=true"
  var defaultJdbcUsername = "user1"
  var defaultJdbcPassword = "user1"
  var defaultDatastore = "sql"
  private var default_http_format = "json" // "html" "direct"
  private var _title = "g3"
  //
  val messageStore: G3MessageStore = new G3MessageStore()
  val authentications: Map[String, G3Authentication] = new HashMap[String, G3Authentication]()
  private val _jdbc_connections = new HashMap[String, java.sql.Connection]
  private val _result_sets = new ArrayBuffer[ResultSet]
  private val _entities = new ArrayBuffer[GEntity]
  private val _contents = new ArrayBuffer[GContent]
  //
  private val _root = new G3RootNode(this)
  private val subscription_candidates = new HashMap[String, mutable.Set[SubscribeChannel]] with MultiMap[String, SubscribeChannel]
  val subscriptions = new HashMap[G3Channel, mutable.Set[SubscribeChannel]] with MultiMap[G3Channel, SubscribeChannel]
  private val node_stack = new Stack[G3Node]
  private val pipe_stack = new Stack[G3Pipe]
  node_stack.push(_root)
//  private val result_parcels = new HashMap[G3TicketId, G3Parcel];
  def current = _root // XXX
  def currentNode:G3Node = node_stack.top

  private var _parameters: Option[GParameterRepository] = None
  def parameters = _parameters.get
  private var _entity_space: Option[GEntitySpace] = None
  lazy val entitySpace = _entity_space.get
  lazy val entityContext = entitySpace.context
  private var _platform: Option[G3Platform] = None
  lazy val platform: G3Platform = _platform.get
  var _is_opened = false
  
  private val _port_channels = new RegexRegistry[G3Channel]
  private val _timer_channels = new ArrayBuffer[(String, G3Channel)]

  def setParameters(params: GParameterRepository) {
    _parameters = Some(params)
  }

  def getParameter(key: String): Option[AnyRef] = {
    parameters.get(key).asInstanceOf[Option[AnyRef]] // XXX
  }

  def setEntitySpace(space: GEntitySpace) {
    require (!_is_opened)
    _entity_space = Some(space)
  }

  def setPlatform(pf: G3Platform) {
    require (!_is_opened)
    require (pf != null)
    _platform = Some(pf)
  }

  def findPortChannel(uri: String): Option[G3Channel] = {
    find_port_channel(uri)
  }

  def findPortChannel(args: List[AnyRef]): Option[G3Channel] = {
    if (args.isEmpty) None
    else find_port_channel(args(0).toString)
  }

  private def find_port_channel(anUri: String): Option[G3Channel] = {
    val uri = anUri match {
      case "/~" => "/"
      case _ => anUri
    }
    if (uri == "/") {
      _port_channels.getOption("/")
    } else {
      _port_channels.find(uri)
    }
  }

  def portChannels = _port_channels.registry collect {
    case (_, pc: PortChannel) => pc
  }
/*
  def findPortChannelRemainder(uri: String): Option[(G3Channel, String)] = {
    find_port_channel_remainder(uri)
  }

  def findPortChannelRemainder(args: List[Any]): Option[(G3Channel, String)] = {
    if (args.isEmpty) None
    else find_port_channel_remainder(args(0).toString)
  }

  private def find_port_channel_remainder(anUri: String): Option[(G3Channel, String)] = {
    val uri = anUri match {
      case "/~" => "/"
      case _ => anUri
    }
    _port_channels.find2(uri) match {
      case Some((regex, ch)) => {
        val key = regex.toString
        if (key == "/") {
          if (uri == "/") Some((ch, ""))
          else None
        } else {
          Some((ch, key.substring(regex.findFirstIn(key).get.length)))
        }
      }
      case None => None
    }
  }
*/
  def findTimerChannels(): List[G3Channel] = {
    _timer_channels.map(_._2).toList
  }

  //
  def open() {
    platform.open()
    resolve_subscriptions()
    _root.open()
    NullAdapter.open(this)
    _is_opened = true
  }

  def close() {
    _result_sets.foreach(_.close())
    logDebug(this, "Close ResultSets: " + _result_sets.size)
    _result_sets.clear()
    _jdbc_connections.values.foreach(_.close())
    logDebug(this, "Close JDBC Connections: " + _jdbc_connections.size)
    _jdbc_connections.clear()
    _entities.foreach(_.close())
    logDebug(this, "Close GEntities: " + _entities.size)
    _entities.clear()
    _contents.foreach(_.close())
    logDebug(this, "Close GContents: " + _contents.size)
    _contents.clear()
    _root.close()
    platform.close()
    _is_opened = false
  }

  def commit() {
    _jdbc_connections.values.foreach(_.commit())
    logDebug(this, "Commit JDBC Connections: " + _jdbc_connections.size)
    _entities.foreach(_.commit())
    logDebug(this, "Commit GEntities: " + _entities.size)
    _contents.foreach(_.commit())
    logDebug(this, "Commit GContents: " + _contents.size)
    logDebug(this, "Close ResultSets: " + _result_sets.size)
    _result_sets.foreach(_.close())
    _result_sets.clear()
  }

  def rollback() {
    _jdbc_connections.values.foreach(_.rollback())
    logDebug(this, "Rollback JDBC Connections: " + _jdbc_connections.size)
    _entities.foreach(_.rollback())
    logDebug(this, "Rollback GEntities: " + _entities.size)
    logDebug(this, "Rollback GContents: " + _contents.size)
    logDebug(this, "Close ResultSets: " + _result_sets.size)
    _result_sets.foreach(_.close())
    _result_sets.clear()
  }

  //
  private def resolve_subscriptions() {
//    println("resolve_subscriptions")
    for ((name, subscribers) <- subscription_candidates) {
//      println("resolve_subscriptions:" + name + " -> " + subscribers)
      lookup_channel(name) match {
        case Some(publisher) => subscriptions.put(publisher, subscribers)
        case None => throw new G3ApplicationException("No channel: " + name, "g3://XXX")
      }
    }
  }

  //
  def pushNode(node: G3Node) {
    node_stack.push(node)
  }

  def popNode() {
    node_stack.pop()
  }

  def pushChannelNode(node: G3ChannelNode) {
    node_stack.push(node)
    pushPipe(node.channel)
  }

  def popChannelNode() {
    popPipe()
    node_stack.pop()
  }

  def currentPipe:G3Pipe = pipe_stack.top

  def pushPipe(pipe: G3Pipe) {
    pipe_stack.push(pipe)
  }

  def popPipe() {
    pipe_stack.pop()
  }

  // from G3Channel.channel_stimulus
  def createUnitofworkSignalParcel(title:String, channel: G3Channel)(signal: AnyRef, session: G3Session):(G3Parcel, G3Ticket) = {
    def make_properties = {
      signal match {
        case cmd: HttpCommand => cmd.request match {
          case Some(req) => Map("servlet_request" -> req,
                                "signal" -> signal)
          case None => Map("signal" -> signal)
        }
        case _ => Map("signal" -> signal)
      }
    }
    val uowId = G3Id.createUnitofworkId()
    val ticket = new G3Ticket(
      G3Id.createTicketId(),
      None,
      None,
      Some(uowId),
      this
    )
    val parcelId = G3Id.createParcelId()
    val time = parcelId.time
    val parcel = new G3Parcel(
      parcelId,
      title,
      channel.id,
      time,
      time,
      //
      Nil,
      //
      Nil,
      None,
      None,
      Nil,
      0,
      //
      Some(session),
      None,
      None,
      Some(uowId),
      //
      signal,
      Nil,
      make_properties,
      //
      Nil,
      this
    )
    (parcel, ticket)
  }

  // from G3Channel.channel_send
  def createUnitofworkParcel(title:String, channel: G3Channel)(source: G3Parcel):(G3Parcel, G3Ticket) = {
    val uowId = G3Id.createUnitofworkId()
    val ticket = new G3Ticket(
      G3Id.createTicketId(),
      source.job,
      source.transaction,
      Some(uowId),
      this
    )
    val parcelId = G3Id.createParcelId()
    val time = parcelId.time
    val parcel = new G3Parcel(
      parcelId,
      title,
      channel.id,
      time,
      time,
      //
      Nil,
      //
      List(source.id),
      None,
      None,
      Nil,
      0,
      //
      source.session,
      source.job,
      None,
      Some(uowId),
      //
      source.content,
      source.attachments,
      source.properties,
      //
      source.tickets,
      source.context
    )
    (parcel, ticket)
  }

  // from G3Join
  def createContentParcel(title: String)(content: AnyRef, source: G3Parcel): G3Parcel = {
    createContentAttachmentsParcel(title)(content, source.attachments, source)
  }

  // from GComponent.action
  def createContentAttachmentsParcel(title: String)(content: AnyRef, attachments: List[AnyRef], source: G3Parcel): G3Parcel = {
    val parcelId = G3Id.createParcelId()
    val time = parcelId.time
    new G3Parcel(
      parcelId,
      title,
      source.author,
      time,
      time,
      //
      Nil,
      //
      List(source.id),
      source.parent,
      source.base,
      source.children,
      source.sequenceNo,
      //
      source.session,
      source.job,
      source.transaction,
      source.unitofwork,
      //
      content,
      attachments,
      source.properties,
      //
      source.tickets,
      source.context
    )
  }

  // from G3Actionable
  def createExceptionParcel(title: String)(exception: Exception, source: G3Parcel): G3Parcel = {
    val parcelId = G3Id.createParcelId()
    val time = parcelId.time
    new G3Parcel(
      parcelId,
      title,
      source.author,
      time,
      time,
      //
      Nil,
      //
      List(source.id),
      source.parent,
      source.base,
      source.children,
      source.sequenceNo,
      //
      source.session,
      source.job,
      source.transaction,
      source.unitofwork,
      //
      Exceptioned(source, exception),
      Nil, // XXX
      source.properties,
      //
      source.tickets,
      source.context
    )
  }

  // from G3Split
  def createSplitParcels(title: String)(contents: List[AnyRef], source: G3Parcel): List[G3Parcel] = {
    require (contents.length > 1)

    val length = contents.length
    val topId = G3Id.createParcelId()
    val childIds = (for (i <- 1 until contents.length) yield {
      G3Id.createParcelId()
    }).toList

//    println("childIds = " + childIds)

    def make_top_parcel() = {
      val time = topId.time
      new G3Parcel(
        topId,
        title,
        source.author,
        time,
        time,
        //
        Nil,
        //
        List(source.id),
        None,
        Some(topId),
        childIds.toList,
        0,
        //
        source.session,
        source.job,
        source.transaction,
        source.unitofwork,
        //
        contents(0),
        source.attachments,
        source.properties,
        //
        source.tickets,
        source.context
      )
    }

    def make_child_parcel(index: Int, id: G3ParcelId, content: AnyRef) = {
      val time = id.time
      new G3Parcel(
        id,
        title,
        source.author,
        time,
        time,
        //
        Nil,
        //
        List(source.id),
        Some(topId),
        Some(id),
        Nil,
        index,
        //
        source.session,
        source.job,
        source.transaction,
        source.unitofwork,
        //
        content,
        source.attachments,
        source.properties,
        //
        source.tickets,
        source.context
      )
    }

//    println("childIds2 = " + childIds)

    val top = make_top_parcel()
    val children = for (i <- 1 until contents.length) yield {
      make_child_parcel(i, childIds(i - 1), contents(i))
    }
    top :: children.toList
  }

  def createAggregatedParcel(title: String)(content: AnyRef, attachments: List[AnyRef], sources: List[G3Parcel]): G3Parcel = {
    val parcelId = G3Id.createParcelId()
    val time = parcelId.time
    val source = sources(0)
    new G3Parcel(
      parcelId,
      title,
      source.author,
      time,
      time,
      //
      Nil,
      //
      sources.map(_.id),
      None,
      None,
      Nil,
      0,
      //
      source.session,
      source.job,
      source.transaction,
      source.unitofwork,
      //
      content,
      attachments,
      Map(),
      //
      source.tickets, // XXX sources.tickets
      source.context
    )
  }

  private def accept_parcel(title: String)(parcel: G3Parcel, source: G3Parcel): G3Parcel = {
//    println("accept_parcel: " + parcel.content)
    val parcelId = G3Id.createParcelId()
    val time = parcelId.time
    new G3Parcel(
      parcelId,
      title,
      source.author,
      time,
      time,
      //
      Nil,
      //
      List(parcel.id),
      None,
      None,
      Nil,
      source.sequenceNo,
      //
      source.session,
      source.job,
      source.transaction,
      source.unitofwork,
      //
      parcel.content,
      parcel.attachments,
      parcel.properties,
      //
      source.tickets,
      source.context
    )
  }

  private def accept_ticket(title:String)(ticket: G3Ticket, source: G3Parcel): (G3Ticket, G3Parcel) = {
    val parcelId = G3Id.createParcelId()
    val time = parcelId.time
    (
      ticket,
      new G3Parcel(
        parcelId,
        title,
        source.author,
        time,
        time,
        //
        Nil,
        //
        List(source.id),
        source.parent,
        source.base,
        source.children,
        source.sequenceNo,
        //
        source.session,
        source.job,
        source.transaction,
        source.unitofwork,
        //
        source.content,
        source.attachments,
        source.properties,
        ticket :: source.tickets,
        source.context
      )
    )
  }

  private def accept_tickets(title: String)(tickets: List[G3Ticket], source: G3Parcel): (List[G3Ticket], G3Parcel) = {
    val parcelId = G3Id.createParcelId()
    val time = parcelId.time
    (
      tickets,
      new G3Parcel(
        parcelId,
        title,
        source.author,
        time,
        time,
        //
        Nil,
        //
        List(source.id),
        source.parent,
        source.base,
        source.children,
        source.sequenceNo,
        //
        source.session,
        source.job,
        source.transaction,
        source.unitofwork,
        //
        source.content,
        source.attachments,
        source.properties,
        //
        tickets ::: source.tickets,
        source.context
      )
    )
  }

  def send(dest: String, parcel: G3Parcel): (G3Ticket, G3Parcel) = {
//    println("G3Context.send(" + dest + ") = " + parcel)
    lookup_channel(dest) match {
      case Some(channel) => accept_ticket("send")(channel.send(parcel), parcel)
      case None => throw new G3ApplicationException("No channel: " + dest, "g3://XXX")
    }
  }

  def sendAll(dests: List[String], parcel: G3Parcel): (List[G3Ticket], G3Parcel) = {
//    println("G3Context.sendAll(" + dests.mkString(",") + ") = " + parcel)
    val tickets = for (dest <- dests) yield {
      lookup_channel(dest) match {
        case Some(channel) => channel.send(parcel)
        case None => throw new G3ApplicationException("No channel: " + dest, "g3://XXX")
      }
    }
    accept_tickets("sendAll")(tickets.toList, parcel)
  }

  def invoke(dest: String, parcel: G3Parcel): G3Parcel = {
//    println("G3Context.invoke")
    lookup_channel(dest) match {
      case Some(channel) => accept_parcel("invoke")(channel.invoke(parcel), parcel)
      case None => throw new G3ApplicationException("No channel: " + dest, "g3:///XXX")
    }
  }

  private def lookup_node(path: String): Option[G3Node] = {
/*
    if (path.startsWith("http:")) {
      return Some(new G3ServiceNode(Symbol(createUUID.toString), path, this))
    }
*/
    val node = if (path.startsWith("/")) {
      _root
    } else {
      current
    }
    val comps = path.split("/").filter(_ != "").toList
    lookup_node(comps, node)
  }

  private def lookup_node(comps: List[String], container: G3Node): Option[G3Node] = {
//    println("lookup = " + comps.mkString("[", ",", "]") + ", " + container.children.find(_.name == comps.head))
    container.children.find(_.name == comps.head).flatMap {
      child => comps.tail match {
        case Nil => Some(child)
        case tail => lookup_node(tail, child)
      }
    }
  }

  private def lookup_channel(path: String): Option[G3Channel] = {
    lookup_node(path) match {
      case Some(node: G3ChannelNode) => Some(node.channel)
      case _ => None
    }
  }

  def subscribe(publishers: List[String], channel: SubscribeChannel) {
//    println("subscribe = " + publishers)
    for (publisher <- publishers) {
      subscription_candidates.addBinding(publisher, channel)
    } 
  }

  def sendTerminal(parcel: G3Parcel, channel: G3Channel) {
//    println("terminal = " + parcel)
    if (parcel.unitofwork.isDefined) {
//      println("terminal uow = " + parcel.unitofwork)
      val uow = parcel.unitofwork.get
      if (channel.isComplete(uow)) {
        channel.postParcel(parcel)
        messageStore.post(parcel)
        messageStore.setUnitofworkResult(uow, parcel)
      }
    }
    subscriptions.get(channel) match {
      case Some(subscribers) => subscribers.foreach {
        _.send(parcel) // XXX
      }
      case None => {}
    }
  }

  def resultParcel(ticket: G3Ticket): G3Parcel = {
    messageStore.getResultParcel(ticket).get
  }

  def getResultParcel(ticket: G3Ticket): Option[G3Parcel] = {
    messageStore.getResultParcel(ticket)
  }

  def invokeBean(className: String, methodName: Option[String], args: Array[AnyRef]): AnyRef = {
    def is_public(m: Method) = (m.getModifiers & Modifier.PUBLIC) == Modifier.PUBLIC

    val klass = Class.forName(className)
    val method = methodName match {
      case Some(name) => {
        klass.getMethods.filter(is_public)
        .find {m => m.getName() == name} match {
          case Some(method) => method
          case None => throw new G3ComponentException("XXX", "g3://XXX")
        }
      }
      case None => {
        klass.getMethods.filter(is_public)(0)
      }
    }
    method.invoke(args)
  }

  def getContent(path: String): Option[AnyRef] = {
    lookup_node(path) match {
      case Some(node: G3ChannelNode) => node.channel.getContent
      case None => throw new IllegalArgumentException("No channel: " + path)
    }
  }

  // ChannelFactory
  def createPlainChannel() = {
    new PlainChannel(G3Id.createChannelId(), this)
  }

  def createStartChannel() = {
    new StartChannel(G3Id.createChannelId(), this)
  }

  def createPortChannel(path: String, description: Option[Description]) = {
    register_port(path, new PortChannel(this, G3Id.createChannelId(), path, description))
  }

  private def register_port(pattern: String, channel: G3Channel) = {
    _port_channels += Tuple2(pattern.r, channel)
    channel
  }

  def createTimerChannel(rule: String) = {
    register_timer(rule, createChannel(new TimerAdapter(rule)))
  }

  private def register_timer(rule: String, channel: G3Channel) = {
    _timer_channels += Tuple2(rule, channel)
    channel
  }

  def createWatchChannel(rule: String) = {
    error("not implemented yet.")
  }

  def createSubscribeChannel(publishers: List[String]) = {
    val channel = new SubscribeChannel(G3Id.createChannelId(), publishers, this)
    subscribe(publishers, channel)
    channel
  }

  def createAgentChannel(activity: PartialFunction[AnyRef, AnyRef]) = {
    new AgentChannel(G3Id.createChannelId(), activity, this)
  }

  def createAgent2Channel(activity: G3AgentContext => PartialFunction[AnyRef, AnyRef]) = {
    new Agent2Channel(G3Id.createChannelId(), activity, this)
  }

  def createServiceChannel(uri: String) = {
    new ServiceChannel(G3Id.createChannelId(), uri, this)
  }

  def createBeanChannel(className: String, methodName: Option[String]) = {
    new BeanChannel(G3Id.createChannelId(), className, methodName, this)
  }

  def createComponentChannel(component: G3Component) = {
    new ComponentChannel(G3Id.createChannelId(), component, this)
  }

  def createEntityChannel(driver: G3EntityDriver) = {
    createChannel(new EntityAdapter(driver))
  }

  def createViewChannel(driver: G3ViewDriver) = {
    createChannel(new ViewAdapter(driver))
  }

  def createEventChannel(driver: G3EventDriver) = {
    createChannel(new EventAdapter(driver))
  }

  def createHtmlChannel(html: Html) = {
    createChannel(new HtmlAdapter(html))
  }

  def createWebSocketChannel(path: String) = {
//    createChannel(new WebSocketAdapter(platform.createWebSocketDriver(path)))
    createChannel(new WebSocketAdapter(new JettyWebSocketDriver()))
  }

  def createChannel(adapter: G3Adapter): G3Channel = {
    new PlainChannel(G3Id.createChannelId(), adapter, this)
  }
/*
  // XXX
  def createEntityChannel() = {
    new EntityChannel(G3Id.createChannelId, this)
  }
*/

  //
  def createDataStoreDriver(uri: String = "",
                            username: String = "", password: String = "",
                            driver: String = ""): G3DataStoreDriver = {
    getDefaultDatastore match {
      case "sql" => new SqlDataStoreDriver(uri, username, password, driver)
      case "appengine" => new AppEngineDataStoreDriver(uri)
      // XXX try driver class name
      case _ => new SqlDataStoreDriver(uri, username, password, driver)
    }
  }

/*
  def createEntityDriver(uri: String = ""): G3DataStoreDriver = {
    new SqlDataStoreDriver(uri)
  }
*/

  def getAuthentication(uri: String): Option[G3Authentication] = {
    authentications.get(uri)
  }

  def putAuthentication(uri: String, auth: G3Authentication) {
    authentications += uri -> auth
  }

  def getDefaultJdbcDriver() = defaultJdbcDriver
  def getDefaultJdbcUrl() = defaultJdbcUrl
  def getDefaultJdbcUsername() = defaultJdbcUsername
  def getDefaultJdbcPassword() = defaultJdbcPassword
  def getDefaultDatastore() = defaultDatastore
  def getDefaultHttpFormat = default_http_format

  def setDefaultJdbcDriver(driver: String) {
    require (driver != null)
    defaultJdbcDriver = driver
  }

  def setDefaultJdbcUrl(url: String) {
    require (url != null)
    defaultJdbcUrl = url
  }

  // XXX username, password

  def setDefaultDatastore(ds: String) {
    defaultDatastore = ds
  }

  def setDefaultHttpFormat(format: String) {
    default_http_format = format
  }

  def getJdbcConnection(
    unitofwork: Option[G3UnitofworkId], // XXX
    uri: String, username: String, password: String, driver: String): java.sql.Connection = {
    _jdbc_connections.get(uri) match {
      case Some(conn) => conn
      case None => {
        val conn = platform.openJdbcConnection(uri, username, password, driver)
        _jdbc_connections += uri -> conn
        conn
      }
    }
  }

  def sqlDatatypeFactory(uri: String) = {
    new DerbyDatatypeFactory // XXX
  }

  def getSqlData(rs: ResultSet, index: Int, columntype: Int): AnyRef = {
    columntype match {
      case ARRAY => rs.getArray(index)
      case BIGINT => throw new UnsupportedOperationException("sql data = " + columntype)
      case BINARY => rs.getBytes(index)
      case BIT => throw new UnsupportedOperationException("sql data = " + columntype)
      case BLOB => rs.getBlob(index)
      case BOOLEAN => rs.getBoolean(index).asInstanceOf[AnyRef]
      case CHAR => throw new UnsupportedOperationException("sql data = " + columntype)
      case CLOB => rs.getClob(index)
      case DATALINK => throw new UnsupportedOperationException("sql data = " + columntype)
      case DATE => rs.getDate(index)
      case DECIMAL => rs.getBigDecimal(index)
      case DISTINCT => throw new UnsupportedOperationException("sql data = " + columntype)
      case DOUBLE => rs.getDouble(index).asInstanceOf[AnyRef]
      case FLOAT => rs.getFloat(index).asInstanceOf[AnyRef]
      case INTEGER => rs.getInt(index).asInstanceOf[AnyRef]
      case JAVA_OBJECT => throw new UnsupportedOperationException("sql data = " + columntype)
      case LONGNVARCHAR => throw new UnsupportedOperationException("sql data = " + columntype)
      case LONGVARBINARY => throw new UnsupportedOperationException("sql data = " + columntype)
      case LONGVARCHAR => throw new UnsupportedOperationException("sql data = " + columntype)
      case NCHAR => throw new UnsupportedOperationException("sql data = " + columntype)
      case NCLOB => throw new UnsupportedOperationException("sql data = " + columntype)
      case NULL => throw new UnsupportedOperationException("sql data = " + columntype)
      case NUMERIC => throw new UnsupportedOperationException("sql data = " + columntype)
      case NVARCHAR => throw new UnsupportedOperationException("sql data = " + columntype)
      case OTHER => throw new UnsupportedOperationException("sql data = " + columntype)
      case REAL => throw new UnsupportedOperationException("sql data = " + columntype)
      case REF => throw new UnsupportedOperationException("sql data = " + columntype)
      case ROWID => throw new UnsupportedOperationException("sql data = " + columntype)
      case SMALLINT => throw new UnsupportedOperationException("sql data = " + columntype)
      case SQLXML => throw new UnsupportedOperationException("sql data = " + columntype)
      case STRUCT => throw new UnsupportedOperationException("sql data = " + columntype)
      case TIME => rs.getTime(index)
      case TIMESTAMP => rs.getTimestamp(index)
      case TINYINT => throw new UnsupportedOperationException("sql data = " + columntype)
      case VARBINARY => throw new UnsupportedOperationException("sql data = " + columntype)
      case VARCHAR => rs.getString(index)
    }
  }

  def makeSqlDataType(datatype: XDatatype,
                      multiplicity: Multiplicity = MOne,
                      facets: List[XFacet] = Nil): sql.SqlDatatype = {
    multiplicity match {
      case MOne => make_sql_datatype_one(datatype, facets)
      case MZeroOne => make_sql_datatype_zeroone(datatype, facets)
      case MOneMore => make_sql_datatype_onemore(datatype, facets)
      case MZeroMore => make_sql_datatype_zeromore(datatype, facets)
    }
  }

  private def make_sql_datatype_one(datatype: XDatatype, facets: List[XFacet]) = {
    datatype match {
      case dt: XString => sql.VARCHAR(1024)
      case dt: XBoolean => sql.BOOLEAN
      case dt: XDecimal => sql.DECIMAL // XXX
      case dt: XFloat => sql.FLOAT
      case dt: XDouble => sql.DOUBLE
      case dt: XDuration => error("not supported yet")
      case dt: XDateTime => sql.TIMESTAMP
      case dt: XTime => sql.TIME
      case dt: XDate => sql.DATE
      case dt: XGYearMonth => sql.CHAR(7)
      case dt: XGYear => sql.CHAR(4)
      case dt: XGMonthDay => sql.CHAR(4)
      case dt: XGDay => sql.CHAR(2)
      case dt: XGMonth => sql.CHAR(2)
      case dt: XHexBinary => sql.BINARY
      case dt: XBase64Binary => sql.BINARY
      case dt: XAnyURI => sql.VARCHAR(64)
      case dt: XToken => sql.VARCHAR(32)
      case dt: XLanguage => sql.VARCHAR(32)
      case dt: XInteger => sql.BIGINT
      case dt: XNonPositiveInteger => sql.BIGINT
      case dt: XNegativeInteger => sql.BIGINT
      case dt: XLong => sql.INTEGER
      case dt: XInt => sql.INTEGER
      case dt: XShort => sql.SMALLINT
      case dt: XByte => sql.SMALLINT
      case dt: XNonNegativeInteger => sql.BIGINT
      case dt: XUnsignedLong => sql.BIGINT
      case dt: XUnsignedInt => sql.INTEGER
      case dt: XUnsignedShort => sql.INTEGER
      case dt: XUnsignedByte => sql.INTEGER
      case dt: XPositiveInteger => sql.BIGINT
      case dt: XEntityReference => makeSqlDataType(dt.datatype)
    }
  }

  private def make_sql_datatype_zeroone(datatype: XDatatype, facets: List[XFacet]) = {
    make_sql_datatype_one(datatype, facets)
  }

  private def make_sql_datatype_onemore(datatype: XDatatype, facets: List[XFacet]) = {
    sql.VARCHAR(1024)
  }

  private def make_sql_datatype_zeromore(datatype: XDatatype, facets: List[XFacet]) = {
    sql.VARCHAR(1024)
  }

  // XXX move to Platform and unify GEntityContext
  private var context_tmp_directory: File = _
  private var context_goldenport_work_directory: File = _
  private var context_work_directory: File = _

  final def createWorkFile(): File = {
    val file = File.createTempFile("goldenport", ".tmp", workDirectory)
    file.deleteOnExit()
    file
  }

  final def createWorkDirectory(): File = {
    val dir = File.createTempFile("goldenport", ".d.tmp", workDirectory)
    dir.delete()
    dir.mkdir()
    dir.deleteOnExit()
    dir
  }

  final def workDirectory: File = {
    if (context_work_directory == null) {
      context_work_directory =
	UFile.createTempDir("goldenport", workRootDirectory)
    }
    context_work_directory
  }

  private def workRootDirectory: File = {
    if (context_goldenport_work_directory == null) {
      context_goldenport_work_directory = new File(tmpDirectory, "goldenport.d")
      if (context_goldenport_work_directory.exists() &&
	  !context_goldenport_work_directory.isDirectory()) {
	throw new IllegalStateException(
	  "%sがディレクトリでありませんでした。".format(context_goldenport_work_directory.toString)) // TODO string
      }
      if (!context_goldenport_work_directory.exists()) {
	context_goldenport_work_directory.mkdir()
      }
    }
    context_goldenport_work_directory
  }

  final def tmpDirectory: File = {
    if (context_tmp_directory == null) {
      val name = System.getProperty("java.io.tmpdir")
      if (name == null) {
	throw new IllegalStateException("java.io.tmpdirが設定されていません。") // TODO string
      }
      context_tmp_directory = new File(name)
      if (!context_tmp_directory.isDirectory()) {
	throw new IllegalStateException(
	  "tmpdirの値%sがディレクトリでありません。".format(context_tmp_directory.toString)) // TODO string
      }
    }
    context_tmp_directory
  }

  final def executeCommand(aCommandLine: String): Process = {
    Runtime.getRuntime().exec(aCommandLine)
  }

  final def executeCommand(aCommandLine: String, envp: Array[String]): Process = {
    Runtime.getRuntime().exec(aCommandLine, envp)
  }

  final def executeCommand(aCommandLine: String, envp: Array[String], dir: File): Process = {
    Runtime.getRuntime().exec(aCommandLine, envp, dir)
  }

  final def getResource(name: String): URL = {
    val loc = new ResourceLocator(name)
    entityContext.lookupResource(loc)
  }

  final def loadClass(name: String): Class[_] = {
    val o: AnyRef = entityContext.newInstance(name)
    o.getClass // XXX
  }

  final def newInstance[T](name: String): T = {
    entityContext.newInstance(name).asInstanceOf[T]
  }

  //
  def compileSource(file: File) {
    throw new UnsupportedOperationException("no scala execution environment")
  }

  def executeScript(file: File) {
    throw new UnsupportedOperationException("no scala execution environment")
  }

  def interpret(line: String) {
    throw new UnsupportedOperationException("no scala execution environment")
  }

  //
  def httpGet(get: http.GET) = platform.httpGet(get)
  def httpPut(put: http.PUT) = platform.httpPut(put)
  def httpPost(post: http.POST) = platform.httpPost(post)
  def httpDelete(delete: http.DELETE) = platform.httpDelete(delete)
  def httpMkcol(mkcol: http.MKCOL) = platform.httpMkcol(mkcol)
  def httpCopy(copy: http.COPY) = platform.httpCopy(copy)
  def httpMove(move: http.MOVE) = platform.httpMove(move)

  def urlFetchString(url: URL, encoding: String = "utf-8"): String = {
    platform.urlFetchString(url, encoding)
  }

  // logging XXX platform logging
  def logInfo(o: AnyRef, message: Any, data: Any = None) {
    if (!is_log_info(o)) return
    platform.logInfo(o, message, data)
  }

  def logEnter(o: AnyRef, name: String, message: Any, data: Any = None) {
    if (!is_log_enter(o)) return
    platform.logEnter(o, name, message, data)
  }

  def logLeave(o: AnyRef, name: String, message: Any, data: Any = None) {
    if (!is_log_leave(o)) return
    platform.logLeave(o, name, message, data)
  }

  def logDebug(o: AnyRef, message: Any, data: Any = None) {
    if (!is_log_debug(o)) return
    platform.logDebug(o, message, data)
  }

  def logInfoData(o: AnyRef, message: Any)(data: => Any) {
    if (!is_log_info(o)) return
    platform.logInfoData(o, message)(data)
  }

  def logEnterData(o: AnyRef, name: String, message: Any)(data: => Any) {
    if (!is_log_enter(o)) return
    platform.logEnterData(o, name, message)(data)
  }

  def logLeaveData(o: AnyRef, name: String, message: Any)(data: => Any) {
    if (!is_log_leave(o)) return
    platform.logLeaveData(o, name, message)(data)
  }

  def logDebugData(o: AnyRef, message: Any)(data: => Any) {
    if (!is_log_debug(o)) return
    platform.logDebugData(o, message)(data)
  }

  private def is_log_info(o: Object): Boolean = {
    true
  }

  private def is_log_enter(o: Object): Boolean = {
    true
  }

  private def is_log_leave(o: Object): Boolean = {
    true
  }

  private def is_log_debug(o: Object): Boolean = {
    true
  }

  // Goldenport Entity
  def reconstitute(uri: String): AnyRef = {
    (reconstituteEntity(uri) orElse
     reconstituteContent(uri)).get
  }

  def reconstitute(file: File): AnyRef = {
    (reconstituteEntity(file) orElse
     reconstituteContent(file) orElse
     Some(new FileBlob(file))).get
  }

  def reconstitute(url: URL): AnyRef = {
    (reconstituteEntity(url) orElse
     reconstituteContent(url) orElse
     Some(new UrlBlob(url))).get
  }

  def reconstituteContent(any: AnyRef): Option[GContent] = {
    val content = entitySpace.makeReferenceContent(any.toString)
    open_content(content)
    Some(content)
  }

  def reconstituteEntity(any: AnyRef): Option[GEntity] = {
    entitySpace.reconstitute(any).map(open_entity)
  }

  def openResultSet(rs: ResultSet) {
    _result_sets += rs
  }

  def createEntity(gentity: GEntity) {
    gentity.create()
    _entities += gentity
    gentity
  }

  def openEntity(gentity: GEntity) {
    open_entity(gentity)
  }

  private def open_content(gcontent: GContent) = {
    gcontent.open()
    _contents += gcontent
    gcontent
  }

  private def open_entity(gentity: GEntity) = {
    gentity.open()
    _entities += gentity
    gentity
  }

  def getDataSource(uri: String): GDataSource = {
    entitySpace.getDataSource(uri)
  }

  def getDataSource(uri: URI): GDataSource = {
    entitySpace.getDataSource(uri)
  }

  def title: String = _title

  def setTitle(title: String) {
    _title = title
  }

  def masterHtml: Html = {
    Html(title, <body>
<header>
<div id="header">
<div id="header-content">
<h1>{title}</h1>
</div>
</div>
</header>
<div id="container">
<aside>
<div id="aside">
<div id="aside-content">
{port_directory}
</div>
</div>
</aside>
<article>
<div id="article">
<div id="article-body">
<div id="article-content"/>
</div>
</div>
</article>
</div>
<footer>
<div id="footer">
<div class="powered">
Powered by g3.
</div>
</div>
</footer>
</body>)
  }

  private def port_directory = {
<ul>{
    for ((title, uri) <- _directory_list) yield {
      <li><a href={uri + ".html"}>{title}</a></li>
    }
}</ul>
  }

  private def _directory_list: List[(SDoc, String)] = {
    def cutslash(tp: (SDoc, String)): (SDoc, String) = {
      val (title, path) = tp
      if (path.startsWith("/")) (title, path.substring(1))
      else tp
    }

    val dir = _port_directory_list
    val dir2 = if (dir.exists(tp => _is_root_path(tp._2))) dir
    else (_top_name, "index") :: dir
    dir2.map(cutslash)
  }

  private def _is_root_path(path: String) = path == "/" || path == "/index"

  private def _port_directory_list: List[(SDoc, String)] = {
    def titlepath(regex: Regex, ch: PortChannel): (SDoc, String) = {
      val path = regex.toString
      val title = ch.title
      (title, path)
    }

    _port_channels.registry.collect {
      case (r, p: PortChannel) if _is_not_regex(r) => titlepath(r, p)
    }.sortBy(_._1.toString)
//    _port_channels.registry.filter(rc => _is_not_regex(rc._1)).map(rc => titlepath(rc._1, rc._2.asInstanceOf[PortChannel]))
//      val a = _port_channels.registry
//      val b = a.filter(rc => _is_not_regex(rc._1))
//      val c = b.map(rc => titlepath(rc._1, rc._2.asInstanceOf[PortChannel]))
//      c
  }

  private def _top_name: SDoc = "Top"

  private def _is_not_regex(regex: Regex): Boolean = {
    !List('[', ']', '*').exists(c => regex.toString.contains(c))
  }

  def findPage(uri: String): Option[G3HttpResponse] = {
    uri match {
      case "/_/css/style.css" => Some(new StringHttpResponse(masterCss, "text/css"))
      case "/_/xsl/atomfeed.xsl" => Some(new StringHttpResponse(atomFeedXsl, "text/xsl"))
      case _ => None
    }
  }

  def masterCss: String = """
* {
    color: #ffffff;
    background-color: #595857;
}

h1 {
    color: #2b2b2b;
    background-color: #dcdddd;
    padding-left: 20px;
    border-left: 20px solid #2b2b2b;
}

h1.login {
    color: #2b2b2b;
    background-color: #dcdddd;
    text-align: center;
    padding-left: 0px;
    border-left: 0px;
}

h2 {
    color: #2b2b2b;
    background-color: #dcdddd;
    padding-left: 10px;
    border-left: 15px solid #2b2b2b;
}

h3 {
    color: #dcdddd;
    padding-left: 10px;
    border-left: 10px solid #2b2b2b;
    border-bottom: 2px solid #dcdddd;
}

h4 {
    color: #dcdddd;
    padding-left: 10px;
    border-left: 8px solid #2b2b2b;
    border-bottom: 2px solid #dcdddd;
}

table, th, td {
    margin: 0;
    padding: 0;
    border-collapse: separate;
    border: none;
    border-spacing: 0;
}

table {
    margin: 20px;
    border: 1px solid #eeeeee;
    background: #da9e7f6;
}

caption {
    margin-bottom: 10px;
    padding-left: 10px;
    padding-bottom: 2px;
    border-left: 6px solid #2b2b2b;
    line-height: 1.8;
    font-weight: bold;
    text-align: left;
    color: #dcdddd;
}

th {
    padding: 0.8em 0.5em;
    border-color: #629cdd #8db7e6 #629cdd #eeeeee;
    border-style: solid;
    border-width: 1px;
    color: #2b2b2b;
    background: #d4dcda;
    font-weight: bold:
    text-align: left;
}

td {
    padding: 0.8em 0.5em;
    border-color: #ffffff #81b0e4 #81b0e4 #ffffff;
    border-style: solid;
    border-width: 1px;
    background: #595857;
}

div#header {
//    border-bottom: 1px solid #dcdddd;
}

div#container {
    overflow: hidden;
}

div#aside {
    float: left;
    width: 19%;
}

div#article {
    float: right;
    width: 80%;
    border-left: 1px solid #dcdddd;
}

div#article-body {
    margin-left: 10px;
    margin-right: 10px;
}

div#footer {
    border-top: 1px solid #dcdddd;
    color: #dcdddd;
}

div.powered {
    float: right;
    color: #2b2b2b;
}

div.login {
    text-align:center;
}

table.login {
    margin-left:auto;
    margin-right:auto;
    text-align:justify;
}
"""

  def atomFeedXsl: String = {
    val xml = <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
       	        xmlns:atom="http://www.w3.org/2005/Atom"
                version="1.0">
  <xsl:output method="html"/>

  <xsl:template match="/atom:feed">
    <html>
      <head>
        <title><xsl:value-of select="atom:title"/></title>
        <style type="text/css">{masterCss}</style>
      </head>
      <body>
	<h1>XXX</h1>
	<table>
          <caption>Feed</caption>
	    <tr>
	      <th>Title</th>
	      <td><xsl:value-of select="atom:title"/></td>
	    </tr>
	    <tr>
	      <th>Id</th>
	      <td><xsl:value-of select="atom:id"/></td>
	    </tr>
	    <tr>
	      <th>Updated</th>
	      <td><xsl:value-of select="atom:updated"/></td>
	    </tr>
	</table>

	<table>
	  <caption>Entries</caption>
	  <tr>
	    <th>Title</th>
	    <th>Id</th>
	    <th>Published</th>
	    <th>Updated</th>
	    <th>Content</th>
	    <th>Author</th>
	  </tr>
	  <xsl:for-each select="atom:entry">
	    <tr>
	      <td><xsl:value-of select="atom:title"/></td>
	      <td><xsl:value-of select="atom:id"/></td>
	      <td><xsl:value-of select="atom:published"/></td>
	      <td><xsl:value-of select="atom:updated"/></td>
	      <td><xsl:value-of select="atom:content"/></td>
	      <td><xsl:value-of select="atom:author"/></td>
	    </tr>
	  </xsl:for-each>
	</table>
      </body>
    </html>
  </xsl:template>

</xsl:stylesheet>

    xml.toString
  }
}

abstract class G3SubContext extends G3Context {
  lazy val messageStore: G3MessageStore = parent.messageStore
  lazy val entitySpace: GEntitySpace = parent.entitySpace
  lazy val entityContext: GEntityContext = parent.entityContext

  private var _parent: Option[G3Context] = None

  def parent = _parent.get
  def current = parent.current
  def currentNode: G3Node = parent.currentNode

  def setParent(parent: G3Context) {
    _parent = Some(parent)
  }

  def setParameters(params: GParameterRepository) {
    parent.setParameters(params)
  }

  def getParameter(key: String): Option[Any] = {
    parent.getParameter(key)
  }

  def setEntitySpace(space: GEntitySpace) {
    parent.setEntitySpace(space)
  }

  def setPlatform(pf: G3Platform) = {
    parent.setPlatform(pf)
  }

  def findPortChannel(uri: String) = {
    parent.findPortChannel(uri)
  }

  def findPortChannel(args: List[AnyRef]) = {
    parent.findPortChannel(args)
  }

  def portChannels: List[PortChannel] = {
    parent.portChannels
  }

  def findTimerChannels() = {
    parent.findTimerChannels()
  }

//
  def open() = {
    parent.open()
  }

  def close() = {
    parent.close()
  }

  def commit() = {
    parent.commit()
  }

  def rollback() = {
    parent.rollback()
  }

  //
  def pushNode(node: G3Node) = {
    parent.pushNode(node)
  }

  def popNode() = {
    parent.popNode()
  }

  def pushChannelNode(node: G3ChannelNode) = {
    parent.pushChannelNode(node)
  }

  def popChannelNode() = {
    parent.popChannelNode()
  }

  def currentPipe:G3Pipe = {
    parent.currentPipe
  }

  def pushPipe(pipe: G3Pipe) = {
    parent.pushPipe(pipe)
  }

  def popPipe() = {
    parent.popPipe()
  }

  // from G3Channel.channel_stimulus
  def createUnitofworkSignalParcel(title:String, channel: G3Channel)(signal: AnyRef, session: G3Session):(G3Parcel, G3Ticket) = {
    parent.createUnitofworkSignalParcel(title, channel)(signal, session)
  }

  // from G3Channel.channel_send
  def createUnitofworkParcel(title:String, channel: G3Channel)(source: G3Parcel):(G3Parcel, G3Ticket) = {
    parent.createUnitofworkParcel(title, channel)(source)
  }

  // from G3Join
  def createContentParcel(title: String)(content: AnyRef, source: G3Parcel): G3Parcel = {
    parent.createContentParcel(title)(content, source)
  }

  // from GComponent.action
  def createContentAttachmentsParcel(title: String)(content: AnyRef, attachments: List[AnyRef], source: G3Parcel): G3Parcel = {
    parent.createContentAttachmentsParcel(title)(content, attachments, source)
  }

  // from G3Actionable
  def createExceptionParcel(title: String)(exception: Exception, source: G3Parcel): G3Parcel = {
    parent.createExceptionParcel(title)(exception, source)
  }


  // from G3Split
  def createSplitParcels(title: String)(contents: List[AnyRef], source: G3Parcel): List[G3Parcel] = {
    parent.createSplitParcels(title)(contents, source)
  }

  def createAggregatedParcel(title: String)(content: AnyRef, attachments: List[AnyRef], sources: List[G3Parcel]): G3Parcel = {
    parent.createAggregatedParcel(title)(content, attachments, sources)
  }

  def send(dest: String, parcel: G3Parcel): (G3Ticket, G3Parcel) = {
    parent.send(dest, parcel)
  }

  def sendAll(dests: List[String], parcel: G3Parcel): (List[G3Ticket], G3Parcel) = {
    parent.sendAll(dests, parcel)
  }

  def invoke(dest: String, parcel: G3Parcel): G3Parcel = {
    parent.invoke(dest, parcel)
  }

  def subscribe(publishers: List[String], channel: SubscribeChannel) = {
    parent.subscribe(publishers, channel)
  }

  def sendTerminal(parcel: G3Parcel, channel: G3Channel) = {
    parent.sendTerminal(parcel, channel)
  }

  def resultParcel(ticket: G3Ticket): G3Parcel = {
    parent.resultParcel(ticket)
  }

  def getResultParcel(ticket: G3Ticket): Option[G3Parcel] = {
    parent.getResultParcel(ticket)
  }

  def invokeBean(className: String, methodName: Option[String], args: Array[AnyRef]): AnyRef = {
    parent.invokeBean(className, methodName, args)
  }

  def getContent(path: String): Option[AnyRef] = {
    parent.getContent(path)
  }

  // ChannelFactory
  def createPlainChannel() = {
    parent.createPlainChannel()
  }

  def createStartChannel() = {
    parent.createStartChannel()
  }

  def createPortChannel(rule: String, description: Option[Description]) = {
    parent.createPortChannel(rule, description)
  }

  def createTimerChannel(rule: String) = {
    parent.createTimerChannel(rule)
  }

  def createWatchChannel(rule: String) = {
    parent.createWatchChannel(rule)
  }

  def createSubscribeChannel(publishers: List[String]) = {
    parent.createSubscribeChannel(publishers)
  }

  def createAgentChannel(activity: PartialFunction[AnyRef, AnyRef]) = {
    parent.createAgentChannel(activity)
  }

  def createAgent2Channel(activity: G3AgentContext => PartialFunction[AnyRef, AnyRef]) = {
    parent.createAgent2Channel(activity)
  }

  def createServiceChannel(uri: String) = {
    parent.createServiceChannel(uri)
  }

  def createBeanChannel(className: String, methodName: Option[String]) = {
    parent.createBeanChannel(className, methodName)
  }

  def createComponentChannel(component: G3Component) = {
    parent.createComponentChannel(component)
  }

  def createEntityChannel(driver: G3EntityDriver) = {
    parent.createEntityChannel(driver)
  }

  def createViewChannel(driver: G3ViewDriver) = {
    parent.createViewChannel(driver)
  }

  def createEventChannel(driver: G3EventDriver) = {
    parent.createEventChannel(driver)
  }

  def createHtmlChannel(html: Html) = {
    parent.createHtmlChannel(html)
  }

  def createWebSocketChannel(path: String) = {
    parent.createWebSocketChannel(path)
  }

  def createChannel(adapter: G3Adapter): G3Channel = {
    parent.createChannel(adapter)
  }

  //
  def createDataStoreDriver(uri: String = "",
                            username: String = "", password: String = "",
                            driver: String = ""): G3DataStoreDriver = {
    parent.createDataStoreDriver(uri, username, password, driver)
  }

  def getAuthentication(uri: String): Option[G3Authentication] = {
    parent.getAuthentication(uri)
  }

  def putAuthentication(uri: String, auth: G3Authentication) = {
    parent.putAuthentication(uri, auth)
  }

  def getDefaultJdbcDriver() = {
    parent.getDefaultJdbcDriver()
  }

  def getDefaultJdbcUrl() = {
    parent.getDefaultJdbcUrl()
  }

  def getDefaultJdbcUsername(): String = {
    parent.getDefaultJdbcUsername
  }

  def getDefaultJdbcPassword(): String = {
    parent.getDefaultJdbcPassword
  }

  def getDefaultDatastore(): String = {
    parent.getDefaultDatastore
  }

  def getDefaultHttpFormat(): String = {
    parent.getDefaultHttpFormat
  }

  def setDefaultJdbcDriver(driver: String) {
    parent.setDefaultJdbcDriver(driver)
  }

  def setDefaultJdbcUrl(url: String) {
    parent.setDefaultJdbcUrl(url)
  }

  def setDefaultDatastore(ds: String) {
    parent.setDefaultDatastore(ds)
  }

  def setDefaultHttpFormat(format: String) {
    parent.setDefaultHttpFormat(format)
  }

  def getJdbcConnection(
    unitofwork: Option[G3UnitofworkId], // XXX
    uri: String, username: String, password: String, driver: String): java.sql.Connection = {
    parent.getJdbcConnection(unitofwork, uri, username, password, driver)
  }

  def sqlDatatypeFactory(uri: String): DatatypeFactory = {
    parent.sqlDatatypeFactory(uri)
  }

  def getSqlData(rs: ResultSet, index: Int, columntype: Int): AnyRef = {
    parent.getSqlData(rs, index, columntype)
  }

  def makeSqlDataType(datatype: XDatatype,
                      multiplicity: Multiplicity = MOne,
                      facets: List[XFacet] = Nil): sql.SqlDatatype = {
    parent.makeSqlDataType(datatype, multiplicity, facets)
  }

  def createWorkFile(): File = {
    parent.createWorkFile()
  }

  def createWorkDirectory(): File = {
    parent.createWorkDirectory()
  }

  def workDirectory: File = {
    parent.workDirectory
  }

  def tmpDirectory: File = {
    parent.tmpDirectory
  }

  def executeCommand(aCommandLine: String): Process = {
    parent.executeCommand(aCommandLine)
  }

  def executeCommand(aCommandLine: String, envp: Array[String]): Process = {
    parent.executeCommand(aCommandLine, envp)
  }

  def executeCommand(aCommandLine: String, envp: Array[String], dir: File): Process = {
    parent.executeCommand(aCommandLine, envp, dir)
  }

  def getResource(name: String): URL = {
    parent.getResource(name)
  }

  final def loadClass(name: String): Class[_] = {
    parent.loadClass(name)
  }

  def newInstance[T](name: String): T = {
    parent.newInstance(name)
  }

  //
  def compileSource(file: File) = {
    parent.compileSource(file)
  }

  def executeScript(file: File) = {
    parent.executeScript(file)
  }

  def interpret(line: String) = {
    parent.interpret(line)
  }

  def httpGet(get: http.GET) = {
    parent.httpGet(get)
  }

  def httpPut(put: http.PUT) = {
    parent.httpPut(put)
  }

  def httpPost(post: http.POST) = {
    parent.httpPost(post)
  }

  def httpDelete(delete: http.DELETE) = {
    parent.httpDelete(delete)
  }

  def httpMkcol(mkcol: http.MKCOL) = {
    parent.httpMkcol(mkcol)
  }

  def httpCopy(copy: http.COPY) = {
    parent.httpCopy(copy)
  }

  def httpMove(move: http.MOVE) = {
    parent.httpMove(move)
  }

  def urlFetchString(url: URL, encoding: String = "utf-8"): String = {
    parent.urlFetchString(url, encoding)
  }

  // logging XXX platform logging
  def logInfo(o: AnyRef, message: Any, data: Any = None) = {
    parent.logInfo(o, message, data)
  }

  def logEnter(o: AnyRef, name: String, message: Any, data: Any = None) = {
    parent.logEnter(o, name, message, data)
  }

  def logLeave(o: AnyRef, name: String, message: Any, data: Any = None) = {
    parent.logLeave(o, name, message, data)
  }

  def logDebug(o: AnyRef, message: Any, data: Any = None) = {
    parent.logDebug(o, message, data)
  }

  def logInfoData(o: AnyRef, message: Any)(data: => Any) = {
    parent.logInfoData(o, message)(data)
  }

  def logEnterData(o: AnyRef, name: String, message: Any)(data: => Any) = {
    parent.logEnterData(o, name, message)(data)
  }

  def logLeaveData(o: AnyRef, name: String, message: Any)(data: => Any) = {
    parent.logLeaveData(o, name, message)(data)
  }

  def logDebugData(o: AnyRef, message: Any)(data: => Any) = {
    parent.logDebugData(o, message)(data)
  }

  // Goldenport Entity
  def reconstitute(uri: String): AnyRef = {
    parent.reconstitute(uri)
  }

  def reconstitute(file: File): AnyRef = {
    parent.reconstitute(file)
  }

  def reconstitute(url: URL): AnyRef = {
    parent.reconstitute(url)
  }

  def reconstituteEntity(any: AnyRef): Option[GEntity] = {
    parent.reconstituteEntity(any)
  }

  def openResultSet(rs: ResultSet) {
    parent.openResultSet(rs)
  }

  def createEntity(gentity: GEntity) {
    parent.createEntity(gentity)
  }

  def openEntity(gentity: GEntity) {
    parent.openEntity(gentity)
  }

  def getDataSource(uri: String): GDataSource = {
    parent.getDataSource(uri)
  }

  def getDataSource(uri: URI): GDataSource = {
    parent.getDataSource(uri)
  }

  def title: String = {
    parent.title
  }

  def setTitle(title: String) {
    parent.setTitle(title)
  }

  def masterHtml: Html = {
    parent.masterHtml
  }

  def findPage(uri: String): Option[G3HttpResponse] = {
    parent.findPage(uri)
  }

  def masterCss(): String = {
    parent.masterCss()
  }
}

class PlainContext() extends G3SubContext {
  override def open() {
    val gspace = new DefaultEntitySpace
    gspace.addEntityClass(new CsvEntityClass())
    gspace.addEntityClass(new XMindEntityClass())
    gspace.addEntityClass(new ZipEntityClass())
    parent.setEntitySpace(gspace)
    super.open()
  }
}

class InterpreterContext(val interpreter: Interpreter) extends G3SubContext {
  override def open() {
    val gspace = new DefaultEntitySpace
    gspace.addEntityClass(new CsvEntityClass())
    gspace.addEntityClass(new XMindEntityClass())
    gspace.addEntityClass(new ZipEntityClass())
    parent.setEntitySpace(gspace)
    super.open()
  }

  override def compileSource(file: File) {
    val f = new scala.tools.nsc.io.PlainFile(scala.tools.nsc.io.File(file))
    interpreter.compileSources(new BatchSourceFile(f))
  }

  override def executeScript(file: File) {
    throw new UnsupportedOperationException("script")
  }

  override def interpret(line: String) {
    interpreter.interpret(line)
  }
}

class GoldenportContext(val interpreter: Interpreter,
                        val gSession: GSession,
                        val gContext: GServiceContext) extends G3SubContext {
  override def open() {
    parent.setEntitySpace(gContext.entitySpace)
    super.open()
  }

  override def compileSource(file: File) {
    gContext.compileSource(file)
  }

  override def executeScript(file: File) {
    gContext.executeScript(file)
  }

  override def interpret(line: String) {
    gContext.interpret(line)
  }

  // XXX unifies logging
}

class AppEngineContext() extends G3SubContext {
  override def open() {
    val gspace = new DefaultEntitySpace
    gspace.addEntityClass(new CsvEntityClass())
    gspace.addEntityClass(new XMindEntityClass())
    gspace.addEntityClass(new ZipEntityClass())
    parent.setEntitySpace(gspace)
    super.open()
  }
}

// 2011-01-10
// 2011-11-13
// 2011-12-05
abstract class G3HttpResponse(
    val contentType: String,
    val statusCode: Int = SC_OK,
    val fileName: Option[String] = None) {

  def setResponse(resp: HttpServletResponse) {
    resp.setContentType(contentType)
    resp.setStatus(statusCode)
    for (fn <- fileName) {
      resp.setHeader("Content-Disposition", "attachment; filename=" + fn)
    }
    set_Response(resp)
  }

  protected def set_Response(resp: HttpServletResponse)
}

class StringHttpResponse(
    val content: String,
    contentType: String,
    statusCode: Int = SC_OK) extends G3HttpResponse(contentType, statusCode) {

  protected def set_Response(resp: HttpServletResponse) {
    resp.getWriter().println(content)
  }
}
// 2011-11-13
class ByteArrayHttpResponse(
    val content: Array[Byte],
    contentType: String,
    statusCode: Int = SC_OK,
    fileName: Option[String] = None) extends G3HttpResponse(contentType, statusCode, fileName) {  

  protected def set_Response(resp: HttpServletResponse) {
    resp.getOutputStream().write(content, 0, content.length)
  }
}
// 2011-12-05
class BinaryContentHttpResponse(
    val content: BinaryContent,
    statusCode: Int = SC_OK) extends G3HttpResponse(content.mimeType.map(_.name).getOrElse(mimetype.application_octet_stream),
                                                    statusCode, Option(content.simpleName)) {
  protected def set_Response(resp: HttpServletResponse) {
    content.write(resp.getOutputStream())
  }
}
